/*
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <bits/alltypes.h>
#include <cstdio>
#include <cctype>
#include <mutex>
#include <cstdint>
#include <random>
#include <string>
#include <unordered_map>
#include <sstream>
#include "client.h"
#include "base.h"
#include "tools.h"
#include "boundscheck/third_party_bounds_checking_function/include/securec.h"

static std::unordered_map<std::string, Client*> g_clientMap;
static std::unordered_map<std::string, Client*> g_clientMidMap;
static std::mutex g_mutex;

struct AsyncCallbackInfo {
    napi_env env;
    napi_async_work asyncWork;
    napi_deferred deferred;
    Client* client;
};

struct Message {
    std::string _message;
    std::string _remoteIp;
    int code;
};

namespace {
void FreeXmitData(coap_session_t *session COAP_UNUSED, void *app_ptr)
{
    coap_free(app_ptr);
    return;
}

void TrackNewToken(Client *client, size_t tokenlen, uint8_t *token)
{
    TrackToken *newList =
        static_cast<TrackToken *>(malloc((client->trackedTokensCount + 1) * sizeof(client->trackedTokens[0])));
    if (!newList) {
        return;
    }
    Tools::ParseBuff(newList, (client->trackedTokensCount + 1) * sizeof(client->trackedTokens[0]),
        client->trackedTokens, (client->trackedTokensCount + 1) * sizeof(client->trackedTokens[0]));
    client->trackedTokens = newList;
    client->trackedTokens[client->trackedTokensCount].token = coap_new_binary(tokenlen);
    if (!client->trackedTokens[client->trackedTokensCount].token)
        return;
    Tools::ParseBuff(client->trackedTokens[client->trackedTokensCount].token->s, tokenlen, token, tokenlen);
    client->trackedTokens[client->trackedTokensCount].observe = client->doingObserve;
    client->trackedTokensCount++;
}

int TrackCheckToken(Client *client, coap_bin_const_t *token)
{
    size_t i;
    for (i = 0; i < client->trackedTokensCount; i++) {
        if (coap_binary_equal(token, client->trackedTokens[i].token)) {
            return 1;
        }
    }
    return 0;
}

void TrackFlushToken(Client *client, coap_bin_const_t *token)
{
    size_t i;
    for (i = 0; i < client->trackedTokensCount; i++) {
        if (coap_binary_equal(token, client->trackedTokens[i].token)) {
            if (!client->trackedTokens[i].observe || !client->obsStarted) {
                /* Only remove if not Observing */
                coap_delete_binary(client->trackedTokens[i].token);
                if (client->trackedTokensCount - i > 1) {
                    Tools::MoveBuff(&client->trackedTokens[i],
                                    (client->trackedTokensCount - i - 1) * sizeof(client->trackedTokens[0]),
                                    &client->trackedTokens[i + 1],
                                    (client->trackedTokensCount - i - 1) * sizeof(client->trackedTokens[0]));
                }
                client->trackedTokensCount--;
            }
            break;
        }
    }
}

coap_pdu_t *CoapNewRequest(coap_context_t *ctx,
                           coap_session_t *session,
                           unsigned char *data,
                           size_t length, Client *client)
{
    coap_pdu_t *pdu;
    uint8_t token[8];
    size_t tokenlen;
    (void)ctx;
    if (!(pdu = coap_new_pdu(static_cast<coap_pdu_type_t>(client->msgType),
        static_cast<coap_pdu_code_t>(client->method), session))) {
        FreeXmitData(session, data);
        return nullptr;
    }

    /*
     * Create unique token for this request for handling unsolicited /
     * delayed responses
     */
    coap_session_new_token(session, &tokenlen, token);
    TrackNewToken(client, tokenlen, token);
    if (!coap_add_token(pdu, tokenlen, token)) {
    }

    if (client->optList)
        coap_add_optlist_pdu(pdu, &client->optList);

    if (length) {
        /* Let the underlying libcoap decide how this data should be sent */
        coap_add_data_large_request(session, pdu, length, data,
                                    FreeXmitData, data);
    }
    if (client->mid) {
        LOGI("coap update mid :%d", client->mid);
        coap_pdu_set_mid(pdu, client->mid);
    }
    return pdu;
}

/**
 * Calculates decimal value from hexadecimal ASCII character given in
 * @p c. The caller must ensure that @p c actually represents a valid
 * heaxdecimal character, e.g. with isxdigit(3).
 *
 * @hideinitializer
 */
int HexcharToDec(char c)
{
    int numberNine = 9;
    return ((c)&0x40 ? ((c)&0x0F) + numberNine : ((c)&0x0F));
}

/**
 * Decodes percent-encoded characters while copying the string @p seg
 * of size @p length to @p buf. The caller of this function must
 * ensure that the percent-encodings are correct (i.e. the character
 * '%' is always followed by two hex digits. and that @p buf provides
 * sufficient space to hold the result. This function is supposed to
 * be called by make_decoded_option() only.
 *
 * @param seg     The segment to decode and copy.
 * @param length  Length of @p seg.
 * @param buf     The result buffer.
 */
void DecodeSegment(const uint8_t *seg, size_t length, unsigned char *buf)
{
    int positionTwo = 2;
    int displacementFour = 4;
    while (length--) {
        if (*seg == '%') {
            *buf = (HexcharToDec(seg[1]) << displacementFour) + HexcharToDec(seg[positionTwo]);

            seg += positionTwo;
            length -= positionTwo;
        } else {
            *buf = *seg;
        }

        ++buf;
        ++seg;
    }
}

/**
 * Runs through the given path (or query) segment and checks if
 * percent-encodings are correct. This function returns @c -1 on error
 * or the length of @p s when decoded.
 */
int CheckSegment(const uint8_t *s, size_t length)
{
    int n = 0;
    int positionTwo = 2;
    while (length) {
        if (*s == '%') {
            if (length < positionTwo || !(isxdigit(s[1]) && isxdigit(s[positionTwo]))) {
                return -1;
            }
            s += positionTwo;
            length -= positionTwo;
        }
        ++s;
        ++n;
        --length;
    }
    return n;
}

int SetInput(char *text, coap_string_t *buf)
{
    int len = 0;
    len = CheckSegment((unsigned char *)text, strlen(text));
    if (len < 0) {
        return 0;
    }

    buf->s = (unsigned char *)coap_malloc(len);
    if (!buf->s) {
        return 0;
    }

    buf->length = len;
    DecodeSegment((unsigned char *)text, strlen(text), buf->s);
    return 1;
}

int ResolveAddress(const coap_str_const_t *server, struct sockaddr *dst)
{
    struct addrinfo *res;
    struct addrinfo *ainfo;
    struct addrinfo hints;
    static char addrstr[256];
    int error;
    int len = -1;
    int localHostNumber = 9;
    Tools::SetBuff(addrstr, sizeof(addrstr), 0, sizeof(addrstr));
    if (server->length)
        Tools::ParseBuff(addrstr, server->length, server->s, server->length);
    else
        Tools::ParseBuff(addrstr, localHostNumber, "localhost", localHostNumber);

    Tools::SetBuff(&hints, sizeof(hints), 0, sizeof(hints));
    hints.ai_socktype = SOCK_DGRAM;
    hints.ai_family = AF_UNSPEC;

    error = getaddrinfo(addrstr, nullptr, &hints, &res);
    if (error != 0) {
        return error;
    }

    for (ainfo = res; ainfo != nullptr; ainfo = ainfo->ai_next) {
        switch (ainfo->ai_family) {
            case AF_INET6:
            case AF_INET:
                len = static_cast<int>(ainfo->ai_addrlen);
                Tools::ParseBuff(dst, len, ainfo->ai_addr, len);
                freeaddrinfo(res);
                return len;
            default:;
        }
    }

    freeaddrinfo(res);
    return len;
}

std::string UintToHexString(const uint8_t *s, uint32_t size)
{
    if (s == nullptr || size == 0) {
        LOGI("[UintToHexString] input param invalid");
        return "";
    }
    std::string res = "";
    for (int i = 0; i < size; i++) {
        char buffer[3] = {0};
        (void*)sprintf_s(buffer, sizeof(buffer), "%02x", s[i]);
        res += std::string(buffer);
    }
    return res;
}

void CallTsFunction(Client *client, std::string remoteIp, std::string str, CoapCode code)
{
    Message *message = new Message();
    message->_message = str;
    message->_remoteIp = remoteIp;
    message->code = (int)code;
    napi_call_threadsafe_function(client->_callbackFunc, message, napi_tsfn_nonblocking);
}

coap_response_t DellMessageCallback(Client *client, 
									const coap_pdu_t *received, 
									coap_bin_const_t token)
{
    int32_t codeSuccess = 2;
    coap_opt_t *block_opt;
    coap_opt_iterator_t opt_iter;
    size_t len;
    const uint8_t *databuf;
    size_t offset;
    size_t total;
    coap_pdu_code_t rcvCode = coap_pdu_get_code(received);
    client->mid = coap_pdu_get_mid(received);
    coap_opt_filter_t f;
    coap_option_num_t number;
    coap_option_iterator_init(received, &opt_iter, COAP_OPT_ALL);
    coap_option_filter_get(&f, number);
    while (coap_option_next(&opt_iter)) {
        client->options.push_back(opt_iter.number);
    }
    /* output the received data, if any */
    if (COAP_RESPONSE_CLASS(rcvCode) == codeSuccess) {
        LOGI("rcv code is ok");
        client->code = CoapCode::SUCCESS;
        /* set obs timer if we have successfully subscribed a resource */
        if (client->doingObserve && !client->obsStarted &&
            coap_check_option(received, COAP_OPTION_OBSERVE, &opt_iter)) {
            client->obsStarted = 1;
            client->obsMs = client->obsSeconds * client->time;
            client->obsMsReset = 1;
        }
        
        coap_opt_t *opt = coap_check_option(received, COAP_OPTION_URI_HOST, &opt_iter);
        if (opt) {
            LOGI("coap return COAP_OPTION_URI_HOST: %s", coap_opt_value(opt));
        }
        if (coap_get_data_large(received, &len, &databuf, &offset, &total)) {
            std::string returnStr = std::string((char *)databuf, len);
            LOGI("coap return message:%s", returnStr.c_str());
            LOGI("message actuality length:%d", len);
            // libcoap返回的databuf有时在正确内容后会带入一些乱码，不是真正想要的数据
            // len的长度是正确内容的长度，根据len截取返回的databuf,以获得正确返回内容
            if (len > returnStr.length()) { len = returnStr.length(); }
            std::string relStr = returnStr.substr(0, len);
            client->clientResultList.push_back(relStr);
            LOGI("get response data:%s", relStr.c_str());
            CallTsFunction(client, client->remoteIp, relStr, client->code);
        }

        /* Check if Block2 option is set */
        block_opt = coap_check_option(received, COAP_OPTION_BLOCK2, &opt_iter);
        if (!client->singleBlockRequested && block_opt) { /* handle Block2 */
            if (coap_opt_block_num(block_opt) == 0) {
                /* See if observe is set in first response */
                client->ready = client->doingObserve ?
                                coap_check_option(received, COAP_OPTION_OBSERVE, &opt_iter) == nullptr : 1;
            }
            if (COAP_OPT_BLOCK_MORE(block_opt)) {
                client->doingGettingBlock = 1;
            } else {
                client->doingGettingBlock = 0;
                TrackFlushToken(client, &token);
            }
            return COAP_RESPONSE_OK;
        }
    } else { /* no 2.05 */
        LOGI("no 2.05");
    }
    if (!client->isMcast) { TrackFlushToken(client, &token); }

    /* our job is done, we can exit at any time */
    client->ready = client->doingObserve ?
                    coap_check_option(received, COAP_OPTION_OBSERVE, &opt_iter) == nullptr : 1;
    return COAP_RESPONSE_OK;
}

void GetRemoteInfo(coap_session_t *session, Client *client) 
{
    if (!client->needRemoteInfo) {
        LOGI("not need GetRemoteInfo");
    }
    if (session) {
        const coap_address_t *address = coap_session_get_addr_remote(const_cast<coap_session_t *>(session));
        LOGI("MessageHandler session is sa: %d", address->addr.sa.sa_family);
        switch (address->addr.sa.sa_family) {
	        case AF_INET:
	            client->remoteIp = inet_ntoa(address->addr.sin.sin_addr);
	            /* create context for IPv4 */
	            break;
	        case AF_INET6:
	            LOGI("MessageHandler session remoteIp not support IPv6");
	            client->remoteIp = "";
	            break;
	        default:;    
        }
        LOGI("MessageHandler session remoteIp: %s", client->remoteIp.c_str());
    } else {
        LOGI("MessageHandler session is invalid");
    }
}

static void thread_finalize_cb(napi_env env, void *finalize_data, void *finalize_hint) {}

static void threadsafe_function_call_js(napi_env env, napi_value js_callback, void *context, void *data)
{
    LOGI("threadsafe_function_call_js start");
    auto message = reinterpret_cast<Message *>(data);
    napi_value arr;
    napi_value undefined;
    napi_create_array(env, &arr);
    napi_get_undefined(env, &undefined);
    
    napi_value value;
    napi_create_string_utf8(env, message->_message.c_str(), NAPI_AUTO_LENGTH, &value);
    napi_set_element(env, arr, 0, value);
    
    if (message->_remoteIp != "") {
        napi_value ip;
        napi_create_string_utf8(env, message->_remoteIp.c_str(), NAPI_AUTO_LENGTH, &ip);
        napi_set_element(env, arr, 1, ip);
    }

    napi_value code;
    napi_create_int32(env, message->code, &code);
    
    napi_value obj;
    napi_create_object(env, &obj);
    napi_set_named_property(env, obj, "code", code);
    napi_set_named_property(env, obj, "message", arr);
    int status = napi_call_function(env, undefined, js_callback, 1, &obj, nullptr);
    if (status != napi_ok) {
        LOGE("napi_call_function failed!");
    }
    delete message;
    message = nullptr;
}

/*
 * Response handler used for coap_send() responses
 */
coap_response_t MessageHandler(coap_session_t *session,
                                      const coap_pdu_t *sent,
                                      const coap_pdu_t *received,
                                      const coap_mid_t id COAP_UNUSED)
{
    coap_pdu_type_t rcvType = coap_pdu_get_type(received);
    coap_bin_const_t token = coap_pdu_get_token(received);
    Client *client = nullptr;
    // libcoap定义的回调函数MessageHandler无法自己传参,参数定义死了,并且mid不是唯一的,无法做为唯一键
    // 所以通过每次请求自己传递token,保证每个请求跟每个回调能够对应起来,保证信息完整性
    std::string tokenStr = UintToHexString(token.s, token.length).c_str();
    LOGI("MessageHandler token receibed:%s", tokenStr.c_str());
    {
        std::lock_guard<std::mutex> lock(g_mutex);
        auto iter = g_clientMidMap.find(tokenStr);
        if (iter != g_clientMidMap.end()) {
            client = g_clientMidMap[tokenStr];
        }
    }

    if (client == nullptr) {
        LOGI("MessageHandler client is null");
        return COAP_RESPONSE_FAIL;
    }
    GetRemoteInfo(session, client);
    /* check if this is a response to our original request */
    if (!TrackCheckToken(client, &token)) {
        /* drop if this was just some message, or send RST in case of notification */
        if (!sent && (rcvType == COAP_MESSAGE_CON || rcvType == COAP_MESSAGE_NON)) {
            /* Cause a CoAP RST to be sent */
            return COAP_RESPONSE_FAIL;
        }
        return COAP_RESPONSE_OK;
    }

    if (rcvType == COAP_MESSAGE_RST) {
        return COAP_RESPONSE_OK;
    }
    DellMessageCallback(client, received, token);
    return COAP_RESPONSE_OK;
}

void setContentType(Client* client, uint16_t key)
{
    coap_optlist_t *node;
    unsigned char i;
    uint16_t value = client->format;
    uint8_t buf[2];
    node = coap_new_optlist(key, coap_encode_var_safe(buf, sizeof(buf), value), buf);
    if (node) {
        coap_insert_optlist(&client->optList, node);
    }
}

uint16_t GetDefaultPort(const coap_uri_t *u)
{
    return coap_uri_scheme_is_secure(u) ? COAPS_DEFAULT_PORT : COAP_DEFAULT_PORT;
}

/**
 * Sets global URI options according to the URI passed as @p arg.
 * This function returns 0 on success or -1 on error.
 *
 * @param arg             The URI string.
 * @param createUriOpts Flags that indicate whether Uri-Host and
 *                        Uri-Port should be suppressed.
 * @return 0 on success, -1 otherwise
 */
int SetNativeUri(Client* client, int createUriOpts)
{
    unsigned char portbuf[2];
    int buffsize = 100;
    unsigned char bufArray[buffsize];
    unsigned char *buf = bufArray;
    size_t buflen;
    int res;

    if (coap_split_uri((unsigned char *)client->serverUri, strlen(client->serverUri), &client->uri) < 0) {
        return -1;
    }

    if (client->uri.scheme == COAP_URI_SCHEME_COAPS && !client->reliable && !coap_dtls_is_supported()) {
        return -1;
    }

    if ((client->uri.scheme == COAP_URI_SCHEME_COAPS_TCP ||
    (client->uri.scheme == COAP_URI_SCHEME_COAPS && client->reliable)) && !coap_tls_is_supported()) {
        return -1;
    }

    if (client->uri.scheme == COAP_URI_SCHEME_COAP_TCP && !coap_tcp_is_supported()) {
        /* coaps+tcp caught above */
        return -1;
    }

    if (client->uri.port != GetDefaultPort(&client->uri) && createUriOpts) {
        coap_insert_optlist(&client->optList, coap_new_optlist(COAP_OPTION_URI_PORT,
            coap_encode_var_safe(portbuf, sizeof(portbuf),
             (client->uri.port & 0xffff)), portbuf));
    }

    if (client->uri.path.length) {
        buflen = buffsize;
        res = coap_split_path(client->uri.path.s, client->uri.path.length, buf, &buflen);
        while (res--) {
            coap_insert_optlist(&client->optList,
                                coap_new_optlist(COAP_OPTION_URI_PATH,
                                                 coap_opt_length(buf),
                                                 coap_opt_value(buf)));

            buf += coap_opt_size(buf);
        }
    }

    if (client->uri.query.length) {
        buflen = buffsize;
        buf = bufArray;
        res = coap_split_query(client->uri.query.s, client->uri.query.length, buf, &buflen);

        while (res--) {
            coap_insert_optlist(&client->optList,
                                coap_new_optlist(COAP_OPTION_URI_QUERY,
                                                 coap_opt_length(buf),
                                                 coap_opt_value(buf)));

            buf += coap_opt_size(buf);
        }
    }

    return 0;
}

/* Called after processing the options from the commandline to set
 * Block1 or Block2 depending on method. */
void SetNativeBlockSize(Client* client)
{
    static unsigned char buf[4]; /* hack: temporarily take encoded bytes */
    uint16_t opt;
    unsigned int optLength;
    int dispalcementThree = 3;
    int dispalcementFour = 4;
    if (client->method != COAP_REQUEST_DELETE) {
        if (client->method == COAP_REQUEST_GET || client->method == COAP_REQUEST_FETCH) {
            opt = COAP_OPTION_BLOCK2;
        } else {
            opt = COAP_OPTION_BLOCK1;
        }
		if (client->payload.length) {
			client->block.m = (opt == COAP_OPTION_BLOCK1) &&
		    ((1ull << (client->block.szx + dispalcementFour)) < client->payload.length);
		} else if (client->payloadBinary.length) {
			client->block.m = (opt == COAP_OPTION_BLOCK1) &&
			((1ull << (client->block.szx + dispalcementFour)) < client->payloadBinary.length);
		}
        optLength = coap_encode_var_safe(buf, sizeof(buf),
                                          (client->block.num << dispalcementFour |
                                            client->block.m << dispalcementThree | client->block.szx));

        coap_insert_optlist(&client->optList, coap_new_optlist(opt, optLength, buf));
    }
}

void SetNativeSubscribe(Client* client, int arg)
{
    uint8_t buf[4];
    client->obsSeconds = arg;
    coap_insert_optlist(&client->optList,
                        coap_new_optlist(COAP_OPTION_OBSERVE,
                                         coap_encode_var_safe(buf, sizeof(buf),
                                                              COAP_OBSERVE_ESTABLISH),
                                         buf));
    client->doingObserve = 1;
}

void SetNativeToken(Client *client, uint8_t *arg, uint32_t dataLen)
{
    int maxNum = 255;
    int position1 = dataLen*2 - 2;
    int position2 = dataLen*2 - 1;
    int subSize = 2;
    client->baseToken.length = Min(sizeof(client->tokenData), dataLen);
    if (client->baseToken.length > 0) {
        Tools::ParseBuff((char *)client->baseToken.s, client->baseToken.length, arg, client->baseToken.length);
    }
    // 给当前Client对象设置token,coap最终透传的token, 会在我们设置dtoken基础上+1
    // 所以客户端保存的token,需要自己加1，255赋值0
    std::string tokenString = UintToHexString(client->baseToken.s, client->baseToken.length).c_str();
    std::string endStr = tokenString.substr(position1, subSize);
    int num = std::stoul(endStr, nullptr, 16);
    if (num == maxNum) {
        num = 0;
    } else {
        num++;
    }
    std::ostringstream ss;
    ss << std::hex << num;
    std::string result = ss.str();
    if (result.size() > 1) {
        tokenString.replace(position1, subSize, result);
    } else {
        tokenString.replace(position1, 1, "0");
        tokenString.replace(position2, 1, &result[0]);
    }
    LOGI("Random tokenString string: %s\n", tokenString.c_str());
    client->tokenStr = tokenString;
}

coap_session_t *OpenSession(
    coap_context_t *ctx,
    coap_proto_t proto,
    coap_address_t *bind_addr,
    coap_address_t *dst)
{
    coap_session_t *session;

    /* Non-encrypted session */
    session = coap_new_client_session(ctx, bind_addr, dst, proto);

    return session;
}

coap_session_t *GetSession(
    coap_context_t *ctx,
    const char *local_addr,
    const char *local_port,
    coap_proto_t proto,
    coap_address_t *dst,
    Client *client)
{
    coap_session_t *session = nullptr;

    client->isMcast = coap_is_mcast(dst);
    if (local_addr) {
        int s;
        struct addrinfo hints;
        struct addrinfo *result = nullptr;
        struct addrinfo *rp;

        Tools::SetBuff(&hints, sizeof(struct addrinfo), 0, sizeof(struct addrinfo));
        hints.ai_family = AF_UNSPEC;                                               /* Allow IPv4 or IPv6 */
        hints.ai_socktype = COAP_PROTO_RELIABLE(proto) ? SOCK_STREAM : SOCK_DGRAM; /* Coap uses UDP */
        hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST | AI_NUMERICSERV | AI_ALL;

        s = getaddrinfo(local_addr, local_port, &hints, &result);
        if (s != 0) {
            return nullptr;
        }

        /* iterate through results until success */
        for (rp = result; rp != nullptr; rp = rp->ai_next) {
            coap_address_t bind_addr;
            if (rp->ai_addrlen <= (socklen_t)sizeof(bind_addr.addr)) {
                coap_address_init(&bind_addr);
                bind_addr.size = (socklen_t)rp->ai_addrlen;
                Tools::ParseBuff(&bind_addr.addr, rp->ai_addrlen, rp->ai_addr, rp->ai_addrlen);
                session = OpenSession(ctx, proto, &bind_addr, dst);
                if (session)
                    break;
            }
        }
        freeaddrinfo(result);
    } else if (local_port) {
        coap_address_t bind_addr;

        coap_address_init(&bind_addr);
        bind_addr.size = dst->size;
        bind_addr.addr.sa.sa_family = dst->addr.sa.sa_family;
        /* port is in same place for IPv4 and IPv6 */
        bind_addr.addr.sin.sin_port = ntohs(atoi(local_port));
        session = OpenSession(ctx, proto, &bind_addr, dst);
    } else {
        session = OpenSession(ctx, proto, nullptr, dst);
    }
    return session;
}

void setData(Client* client, uint8_t *&data, size_t &dataLen)
{
    if (client->payload.length) {
        /* Create some new data to use for this iteration */
        data = static_cast<uint8_t *>(coap_malloc(client->payload.length));
        if (data == nullptr) {
            client->code = CoapCode::OTHER_ERROR;
            return;
        }
        Tools::ParseBuff(data, client->payload.length, client->payload.s, client->payload.length);
        dataLen = client->payload.length;
        LOGI("dataLen:%d", dataLen);
    } else if (client->payloadBinary.length) {
        data = static_cast<uint8_t *>(coap_malloc(client->payloadBinary.length));
        if (data == nullptr) {
            client->code = CoapCode::OTHER_ERROR;
            return;
        }
        Tools::ParseBuff(data, client->payloadBinary.length, client->payloadBinary.s, client->payloadBinary.length);
        dataLen = client->payloadBinary.length;
        LOGI("binary dataLen:%d", dataLen);
    }
}

void coapLogHandler(coap_log_t level, const char *message)
{
    if (level < COAP_LOG_ERR) {
        return LOGF(message);
    }
    if (level == COAP_LOG_ERR) {
        return LOGE(message);
    }
    if (level == COAP_LOG_WARN) {
        return LOGW(message);
    }
    if (level < COAP_LOG_DEBUG) {
        return LOGI(message);
    }
    LOGD(message);
}

void RegisterOptionNative(Client *client, coap_context_t *ctx)
{
    std::list<uint16_t>::iterator iter;
    for (iter = client->options.begin(); iter != client->options.end(); iter++) {
        uint16_t opt = *iter;
        coap_register_option(ctx, opt);
        LOGI("add %d", opt);
    }
}

void RequestNative(Client *client)
{
    coap_set_log_level(COAP_LOG_OSCORE);
    coap_set_log_handler(coapLogHandler);
    LOGI("RequestNative");
    coap_context_t *ctx = nullptr;
    coap_session_t *session = nullptr;
    coap_address_t dst;
    void *addrptr = nullptr;
    int result = -1;
    coap_pdu_t *pdu;
    static coap_str_const_t server;
    uint16_t port = COAP_DEFAULT_PORT;
    char node_str[NI_MAXHOST] = "";
    int res;
    int createUriOpts = 1;
    size_t i;
    coap_uri_scheme_t scheme;
    uint32_t repeatMs = client->repeatDelayMs;
    uint8_t *data = nullptr;
    size_t dataLen = 0;
    client->code = CoapCode::OTHER_ERROR;

    coap_startup();

    if (SetNativeUri(client, createUriOpts) < 0) {
        LOGI("url is incorrect");
        client->code = CoapCode::URL_ERROR;
        client->clientResultList.push_back("url is incorrect");
        goto finish;
    }

    server = client->uri.host;
    port = client->uri.port;
    scheme = client->uri.scheme;

    /* resolve destination address where server should be sent */
    res = ResolveAddress(&server, &dst.addr.sa);
    if (res < 0) {
        LOGI("create res is error");
        client->code = CoapCode::OTHER_ERROR;
        client->clientResultList.push_back("create res is error");
        goto finish;
    }

    ctx = coap_new_context(nullptr);
    if (!ctx) {
        LOGI("create ctx is error");
        client->code = CoapCode::OTHER_ERROR;
        client->clientResultList.push_back("create ctx is error");
        goto finish;
    }

    RegisterOptionNative(client, ctx);
    coap_context_set_keepalive(ctx, 0);
    coap_context_set_block_mode(ctx, client->blockMode);
    if (client->csmMaxMessageSize) {
        coap_context_set_csm_max_message_size(ctx, client->csmMaxMessageSize);
    }
    coap_register_response_handler(ctx, MessageHandler);

    dst.size = res;
    dst.addr.sin.sin_port = htons(port);

    session = GetSession(
        ctx,
        node_str[0] ? node_str : nullptr, client->portStr,
        scheme == COAP_URI_SCHEME_COAP_TCP ?
        COAP_PROTO_TCP : scheme == COAP_URI_SCHEME_COAPS_TCP ?
        COAP_PROTO_TLS : (client->reliable ? scheme == COAP_URI_SCHEME_COAPS ?
        COAP_PROTO_TLS : COAP_PROTO_TCP : scheme == COAP_URI_SCHEME_COAPS ?
        COAP_PROTO_DTLS: COAP_PROTO_UDP), &dst, client);
    if (!session) {
        LOGI("create session is error");
        client->code = CoapCode::NETWORK_ERROR;
        client->clientResultList.push_back("network error");
        goto finish;
    }
    /*
     * Prime the base token value, which coap_session_new_token() will increment
     * every time it is called to get an unique token.
     * [Option '-T token' is used to seed a different value]
     */
    coap_session_init_token(session, client->baseToken.length, client->baseToken.s);
    coap_session_set_mtu(session, client->SessionMtu_);
    coap_session_set_recvbuffersize(session, client->SessionRecvBufferSize_);

    /* add Uri-Host if server address differs from uri.host */

    switch (dst.addr.sa.sa_family) {
        case AF_INET:
            addrptr = &dst.addr.sin.sin_addr;
            /* create context for IPv4 */
            break;
        case AF_INET6:
            addrptr = &dst.addr.sin6.sin6_addr;
            break;
        default:;
    }

    /* construct CoAP message */
    /* set block option if requested at commandline */
    if (client->flags & client->flagsBlock)
        SetNativeBlockSize(client);

    /* Send the first (and may be only PDU) */
    setData(client, data, dataLen);
    if (client->payload.length && client->payloadBinary.length && data == nullptr) {
        goto finish;
    }
    if (!(pdu = CoapNewRequest(ctx, session, data, dataLen, client))) {
        LOGI("create pdu is error");
        client->code = CoapCode::OTHER_ERROR;
        client->clientResultList.push_back("create pdu error");
        goto finish;
    }
    {
        std::lock_guard<std::mutex> lock(g_mutex);
        g_clientMidMap.insert(std::pair<std::string, Client*>(client->tokenStr, client));
    }

    client->waitMs = client->waitSeconds * client->time;

    if (coap_send(session, pdu) == COAP_INVALID_MID) {
        LOGI("coap_send COAP_INVALID_MID");
        client->quit = 1;
    }
    client->repeatCount--;

    while (!client->quit &&
           !(client->ready && !client->trackedTokensCount &&
           !client->isMcast && !client->repeatCount && coap_can_exit(ctx))) {
        uint32_t timeout_ms;
        /*
         * 3 factors determine how long to wait in coap_io_process()
         *   Remaining overall wait time (client->waitMs)
         *   Remaining overall observe unsolicited response time (client->obsMs)
         *   Delay of up to one second before sending off the next request
         */
        if (client->obsMs) {
            timeout_ms = Min(client->waitMs, client->obsMs);
        } else {
            timeout_ms = client->waitMs;
        }
        if (client->repeatCount) {
            timeout_ms = Min(timeout_ms, repeatMs);
        }
        LOGI("coap_io_process start");
        result = coap_io_process(ctx, timeout_ms);
        LOGI("coap_io_process end");
        if (result >= 0) {
            if (client->waitMs > 0) {
                if ((unsigned)result >= client->waitMs) {
                    break;
                } else {
                    client->waitMs -= result;
                }
            }
            if (client->obsMs > 0 && !client->obsMsReset) {
                if ((unsigned)result >= client->obsMs) {
                    for (i = 0; i < client->trackedTokensCount; i++) {
                        if (client->trackedTokens[i].observe) {
                            coap_cancel_observe(session, client->trackedTokens[i].token,
                                static_cast<coap_pdu_type_t>(client->msgType));
                        }
                    }
                    client->doingObserve = 0;
                    client->quit = 1;

                    /* make sure that the obs timer does not fire again */
                    client->obsMs = 0;
                    client->obsSeconds = 0;
                } else {
                    client->obsMs -= result;
                }
            }
            if (client->ready && client->repeatCount) {
                /* Send off next request if appropriate */
                if (repeatMs > (unsigned)result) {
                    repeatMs -= (unsigned)result;
                } else {
                    /* Doing this once a second */
                    repeatMs = client->repeatDelayMs;
                    setData(client, data, dataLen);
                    if (client->payload.length && client->payloadBinary.length && data == nullptr) {
                        goto finish;
                    }
                    if (!(pdu = CoapNewRequest(ctx, session,
                        data, dataLen, client))) {
                    LOGI("create pdu is error");
                    client->code = CoapCode::OTHER_ERROR;
                    client->clientResultList.push_back("create pdu error");
                    goto finish;
                    }
                    client->ready = 0;
                    if (coap_send(session, pdu) == COAP_INVALID_MID) {
                        client->quit = 1;
                    }
                    client->repeatCount--;
                }
            }
            client->obsMsReset = 0;
        }
    }
    LOGI("coap request end");
    result = 0;
	finish: {
	    LOGI("coap request goto finish");
        if (client->code != CoapCode::SUCCESS && client->needCallBack) {
            CallTsFunction(client, client->remoteIp, client->clientResultList.begin()->c_str(), client->code);
        }
	    for (i = 0; i < client->trackedTokensCount; i++) {
	        coap_delete_binary(client->trackedTokens[i].token);
	    }
	    free(client->trackedTokens);
	    client->trackedTokens = nullptr;
	    coap_delete_optlist(client->optList);
	    client->optList = nullptr;
	    coap_session_release(session);
	    coap_free_context(ctx);
	    coap_cleanup();
	    client->payload = {0, nullptr};
	    client->payloadBinary = {0, nullptr};
	    client->format = -1;
	    client->repeatCount = 1;
	    client->trackedTokensCount = 0;
	    client->quit = 0;
	    client->ready = 0;
	    client->isMcast = 0;
	    client->needRemoteInfo = false;
	}
}

int GenerateRandomString(char *buf, int len)
{
    std::string charSet = "0123456789abcdef";
    if (len <= 1) {
        return -1;
    }
    for (int i = 0; i < len - 1; i++) {
        int index = rand() % (charSet.length() - 1);
        buf[i] = charSet[index];
    }
    return 0;
}

Client* getClient(std::string classIdStr)
{
    Client *client = nullptr;
    auto iter = g_clientMap.find(classIdStr);
    if (iter != g_clientMap.end()) {
        client = g_clientMap[classIdStr];
    }
    return client;
}

void DealCallBack(napi_env env, AsyncCallbackInfo *asyncCallbackInfo)
{
    napi_value arr;
    napi_create_array(env, &arr);
    // 组播的请求会有多次调用messageHandler,所以返回值需要一个数组
    int position = 0;
    for (std::list<std::string>::iterator it = asyncCallbackInfo->client->clientResultList.begin();
         it != asyncCallbackInfo->client->clientResultList.end(); it++) {
        napi_value value;
        napi_create_string_utf8(env, it->c_str(), NAPI_AUTO_LENGTH, &value);
        napi_set_element(env, arr, position, value);
        position++;
    }
    napi_value payloadArrayBuffer = nullptr;
    if(!asyncCallbackInfo->client->clientResultList.empty()){
        void *payload = nullptr;
        size_t length = asyncCallbackInfo->client->clientResultList.back().size();
        napi_create_arraybuffer(env, length, &payload, &payloadArrayBuffer);
        memcpy(payload, asyncCallbackInfo->client->clientResultList.back().c_str(),length);
    }
    if (sizeof(asyncCallbackInfo->client->remoteIp) != 0) {
        napi_value value;
        napi_create_string_utf8(env, asyncCallbackInfo->client->remoteIp.c_str(), NAPI_AUTO_LENGTH, &value);
        napi_set_element(env, arr, position, value);
        position++;
    }
    napi_value code;
    napi_create_int32(env, (int)asyncCallbackInfo->client->code, &code);
    napi_value tokenArrayBuffer;
    void* token;
    napi_create_arraybuffer(env, asyncCallbackInfo->client->tokenStr.size(), &token, &tokenArrayBuffer);
    memcpy(token, asyncCallbackInfo->client->tokenStr.c_str(), asyncCallbackInfo->client->tokenStr.size());
    napi_value mid;
    napi_create_int32(env, asyncCallbackInfo->client->mid, &mid);
    napi_value ip;
    napi_create_string_utf8(env, asyncCallbackInfo->client->remoteIp.c_str(), NAPI_AUTO_LENGTH, &ip);
    napi_value options;
    napi_create_array(env, &options);
    int optionnumber = 0;
    for (std::list<std::uint16_t>::iterator it = asyncCallbackInfo->client->options.begin();
         it != asyncCallbackInfo->client->options.end(); ++it) {
        napi_value value;
        napi_create_int32(env, *it, &value);
        napi_set_element(env, options, optionnumber, value);
        optionnumber++;
    }
    napi_value obj;
    napi_create_object(env, &obj);
    napi_set_named_property(env, obj, "code", code);
    napi_set_named_property(env, obj, "message", arr);
    napi_set_named_property(env, obj, "token", tokenArrayBuffer);
    napi_set_named_property(env, obj, "mid", mid);
    napi_set_named_property(env, obj, "ip", ip);
    napi_set_named_property(env, obj, "options", options);
    napi_set_named_property(env, obj, "payload", payloadArrayBuffer);
    napi_resolve_deferred(asyncCallbackInfo->env, asyncCallbackInfo->deferred, obj);
    g_clientMidMap.erase(asyncCallbackInfo->client->tokenStr);
    g_clientMap.erase(asyncCallbackInfo->client->classIdStr);
    LOGI("g_clientMidMap size:%d", g_clientMidMap.size());
    LOGI("g_clientMap size:%d", g_clientMap.size());
    if(asyncCallbackInfo->client->jsRef){
        uint32_t count;
        napi_reference_unref(env, asyncCallbackInfo->client->jsRef, &count);
        if (count == 0) {
            napi_delete_reference(env, asyncCallbackInfo->client->jsRef);
            asyncCallbackInfo->client->jsRef = nullptr;
        }
    }
    napi_delete_async_work(env, asyncCallbackInfo->asyncWork);
    delete asyncCallbackInfo;
    asyncCallbackInfo = nullptr;
}
}


napi_value Client::JsConstructor(napi_env env, napi_callback_info info)
{
    napi_value targetObj = nullptr;
    void *data = nullptr;
    size_t argsNum = 0;
    int32_t tokenLength = 8;
    int32_t subSize = 2;
    napi_value args[2] = { nullptr };
    napi_get_cb_info(env, info, &argsNum, args, &targetObj, &data);
    Client *classBind = new Client();
    uintptr_t classId = reinterpret_cast<uintptr_t>(classBind);
    std::string classIdStrTemp = std::to_string(classId);
    classBind->classIdStr = classIdStrTemp;
    napi_value napiClassId;
    LOGI("classId:%s", classIdStrTemp.c_str());
    srand(atoi(classIdStrTemp.c_str()));
    char buf[17];
    int res = GenerateRandomString(buf, sizeof(buf));
    if (res == -1) {
        LOGE("get token fail");
        return nullptr;
    }
    std::string orignString = std::string(buf, sizeof(buf));
    uint8_t token[8] = {0};
    for (int i = 0; i < orignString.size() - subSize; i = i + subSize) {
        int tokenPos = i / subSize;
        int num = std::stoul(orignString.substr(i, subSize), nullptr, 16);
        token[tokenPos] = num;
    }
    napi_create_string_utf8(env, classIdStrTemp.c_str(), classIdStrTemp.length(), &napiClassId);
    napi_set_named_property(env, targetObj, "classId", napiClassId);
    g_clientMap.insert(std::pair<std::string, Client*>(classIdStrTemp, classBind));
    SetNativeToken(classBind, token, tokenLength);
    napi_value napiToken;
    napi_create_string_utf8(env, classBind->tokenStr.c_str(), classBind->tokenStr.length(), &napiToken);
    napi_set_named_property(env, targetObj, "token", napiToken);
    napi_create_reference(env, targetObj, 0, &classBind->jsRef);
    napi_wrap(
        env, targetObj, classBind,
        [](napi_env env, void *data, void *hint) {
            Client *bind = static_cast<Client *>(data);
            LOGI("delete client : %p", bind);
            if(bind->jsRef){
                uint32_t count;
                napi_reference_unref(env, bind->jsRef, &count);
                if (count == 0) {
                    napi_delete_reference(env, bind->jsRef);
                    bind->jsRef = nullptr;
                }
            }
            delete bind;
            bind = nullptr;
        }, nullptr, nullptr);
    return targetObj;
}
napi_value setNeedRemoteIp(napi_env env, napi_callback_info info)
{
    size_t charLen = 0;
    int len = 1024;
    size_t argc = 2;
    napi_value args[2] = {nullptr};
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    char classId[1024] = {0};
    napi_get_value_string_utf8(env, args[1], classId, len, &charLen);
    std::string classIdStr = classId;
    Client *client = getClient(classIdStr);
    if (!client) {
        return nullptr;
    }
    bool need = false;
    napi_get_value_bool(env, args[0], &need);
    client->needRemoteInfo = need;
    LOGI("message id:%d", client->needRemoteInfo);
    napi_value result = nullptr;
    napi_get_undefined(env, &result);
    return result;
}

namespace {
    bool AddNativeOption(Client *client, int optionNum, char *value)
    {
        std::string data = value;
        std::ostringstream ss;
        std::string result;

        for (int i = 0; i < data.size(); i++) {
            ss << std::hex  << (unsigned int)data[i];
        }
        
        result = ss.str();
        uint8_t tempData[result.length()];
        if (memcpy_s(tempData, result.length(), data.c_str(), data.size()) != EOK) {
            return  false;
        };
        coap_insert_optlist(&client->optList, coap_new_optlist(optionNum, data.size(), tempData));

        return true;
    }

    void AddNativeOption(Client *client, int optionNum, int32_t value)
    {
        std::ostringstream ss;
        std::string result;
        ss << std::hex << value;
        result = ss.str();
        uint8_t buf[result.length()];
        coap_insert_optlist(&client->optList,
                            coap_new_optlist(optionNum, coap_encode_var_safe(buf, sizeof(buf), value), buf));
    }

    void AddNativeOption(Client *client, int optionNum, int64_t value)
    {
        std::ostringstream ss;
        std::string result;
        ss << std::hex << value;
        result = ss.str();
        uint8_t buf[result.length()];
        coap_insert_optlist(&client->optList,
                            coap_new_optlist(optionNum, coap_encode_var_safe8(buf, sizeof(buf), value), buf));
    }
}

napi_value Client::AddOption(napi_env env, napi_callback_info info) {
    size_t charLen = 0;
    int len = 1024;
    size_t argc = 3;
    napi_value args[3] = {nullptr};
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    char classId[1024] = {0};
    napi_get_value_string_utf8(env, args[2], classId, len, &charLen);
    std::string classIdStr = classId;
    Client *client = getClient(classIdStr);
    if (!client) { return nullptr; }
    char content[2048] = {0};
    int optionNum = 0;
    napi_get_value_int32(env, args[0], &optionNum);
    napi_valuetype valuetype1;
    napi_status status = napi_typeof(env, args[1], &valuetype1);
    napi_value result_error = nullptr;
    if (status != napi_ok) {
        napi_create_int32(env, napi_invalid_arg, &result_error);
        return result_error;
    }
    switch (valuetype1) {
        case napi_number: {
                int parValue = 0;
                napi_get_value_int32(env, args[1], &parValue);
                AddNativeOption(client, optionNum, parValue);
            }
            break;
        case napi_bigint: {
                int64_t parValue = 0;
                napi_get_value_int64(env, args[1], &parValue);
                AddNativeOption(client, optionNum, parValue);
            }
            break;
        case napi_string:
            napi_get_value_string_utf8(env, args[1], content, len, &charLen);
            if (!AddNativeOption(client, optionNum, content)) {
                napi_create_int32(env, napi_arraybuffer_expected, &result_error);
                return result_error;
            }
            break;
        default:
            napi_create_int32(env, napi_function_expected, &result_error);
            return result_error;
    }
    napi_get_undefined(env, &result_error);
    return result_error;
}

void setMidType(Client *client, uint16_t key) 
{
    coap_optlist_t *node;
    unsigned char i;
    uint16_t value = client->format;
    uint8_t buf[2];
    node = coap_new_optlist(key, coap_encode_var_safe(buf, sizeof(buf), value), buf);
    if (node) {
        coap_insert_optlist(&client->optList, node);
    }
}

napi_value Client::SetMid(napi_env env, napi_callback_info info) {
    size_t charLen = 0;
    int len = 1024;
    size_t argc = 2;
    napi_value args[2] = {nullptr};
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    char classId[1024] = {0};
    napi_get_value_string_utf8(env, args[1], classId, len, &charLen);
    std::string classIdStr = classId;
    Client *client = getClient(classIdStr);
    if (!client) {
        return nullptr;
    }
    int mids = 0;
    napi_get_value_int32(env, args[0], &mids);
    client->mid = mids;
    LOGI("message id:%d", client->mid);
    napi_value result = nullptr;
    napi_get_undefined(env, &result);
    return result;
}

napi_value Client::SetFormat(napi_env env, napi_callback_info info)
{
    size_t argc = 2;
    size_t charLen = 0;
    int len = 1024;
    napi_value args[2] = {nullptr};
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    // 获取当前类唯一标识classId
    char classId[1024] = {0};
    napi_get_value_string_utf8(env, args[0], classId, len, &charLen);
    std::string classIdStr = classId;
    Client *client = getClient(classIdStr);
    if (!client) {
        return nullptr;
    }
    napi_get_value_int32(env, args[1], &client->format);
    LOGI("format:%d", client->format);
    setContentType(client, COAP_OPTION_CONTENT_TYPE);
    napi_value result = nullptr;
    napi_get_undefined(env, &result);
    return result;
}

napi_value Client::SetWaitSecond(napi_env env, napi_callback_info info)
{
    size_t argc = 2;
    size_t charLen = 0;
    int len = 1024;
    napi_value args[2] = {nullptr};
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    double waitTime;
    napi_get_value_double(env, args[0], &waitTime);
    // 获取当前类唯一标识classId
    char classId[1024] = {0};
    napi_get_value_string_utf8(env, args[1], classId, len, &charLen);
    std::string classIdStr = classId;
    Client *client = getClient(classIdStr);
    if (!client) {
        return nullptr;
    }
    client->waitSeconds = waitTime;
    LOGI("waitSecond:%f", client->waitSeconds);
    napi_value result = nullptr;
    napi_get_undefined(env, &result);
    return result;
}

napi_value Client::SetObsSecond(napi_env env, napi_callback_info info)
{
    size_t argc = 2;
    size_t charLen = 0;
    int len = 1024;
    napi_value args[2] = {nullptr};
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    int obsTime = 0;
    napi_get_value_int32(env, args[0], &obsTime);
    // 获取当前类唯一标识classId
    char classId[1024] = {0};
    napi_get_value_string_utf8(env, args[1], classId, len, &charLen);
    std::string classIdStr = classId;
    Client *client = getClient(classIdStr);
    if (!client) {
        return nullptr;
    }
    SetNativeSubscribe(client, obsTime);
    LOGI("obsSecond:%d", client->obsSeconds);
    napi_value result = nullptr;
    napi_get_undefined(env, &result);
    return result;
}

int SetInputBinary(uint8_t *text, uint32_t dataLen, coap_binary_t *buf) 
{
    if (dataLen < 0) {
        return 0;
    }
    buf->s = (unsigned char *)coap_malloc(dataLen);
    if (!buf->s) {
        return 0;
    }

    buf->length = dataLen;
    if (memcpy_s(buf->s, dataLen, text, dataLen) != EOK) {
        return 0;
    }
    return 1;
}

napi_value Client::SetPayloadBinary(napi_env env, napi_callback_info info) 
{
    LOGI("enter SetPayloadBinary");
    size_t argc = 2;
    size_t charLen = 0;
    int len = 1024;
    napi_value args[2] = {nullptr};
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    void *data;
    size_t dataLen;
    size_t byteOffset;
    napi_typedarray_type dataType;
    napi_value inputBuffer;
    napi_get_typedarray_info(env, args[0], &dataType, &dataLen, &data, &inputBuffer, &byteOffset);
    LOGI("SetPayloadBinary dataLen: %d", dataLen);
    uint8_t *dataTmp = (uint8_t *)(data);
    if (dataTmp == nullptr || dataType != napi_uint8_array) {
        LOGI("SetPayloadBinary dataTmp null");
        return nullptr;
    }
    // 获取当前唯一标识classId
    char classId[1024] = {0};
    napi_get_value_string_utf8(env, args[1], classId, len, &charLen);
    std::string classIdStr = classId;
    Client *client = getClient(classIdStr);
    if (!client) {
        LOGI("SetPayloadBinary client invalid");
        return nullptr;
    }
    SetInputBinary(dataTmp, dataLen, &client->payloadBinary);
    napi_value result = nullptr;
    napi_get_undefined(env, &result);
    LOGI("SetPayloadBinary finish");
    return result;
}

napi_value Client::SetRepeatCount(napi_env env, napi_callback_info info)
{
    size_t argc = 2;
    size_t charLen = 0;
    int len = 1024;
    napi_value args[2] = {nullptr};
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    int count = 1;
    napi_get_value_int32(env, args[0], &count);
    // 获取当前类唯一标识classId
    char classId[1024] = {0};
    napi_get_value_string_utf8(env, args[1], classId, len, &charLen);
    std::string classIdStr = classId;
    Client *client = getClient(classIdStr);
    if (!client) {
        return nullptr;
    }
    client->repeatCount = count;
    LOGI("repeatCount:%d", client->repeatCount);
    napi_value result = nullptr;
    napi_get_undefined(env, &result);
    return result;
}

napi_value Client::SetPort(napi_env env, napi_callback_info info)
{
    char content[32] = {0};
    size_t argc = 2;
    size_t charLen = 0;
    int len = 1024;
    napi_value args[2] = {nullptr};
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    napi_get_value_string_utf8(env, args[0], content, len, &charLen);
    // 获取当前类唯一标识classId
    char classId[1024] = {0};
    napi_get_value_string_utf8(env, args[1], classId, len, &charLen);
    std::string classIdStr = classId;
    Client *client = getClient(classIdStr);
    if (!client) {
        return nullptr;
    }
    client->portStr = content;
    LOGI("port:%s", client->portStr);
    napi_value result = nullptr;
    napi_get_undefined(env, &result);
    return result;
}

napi_value Client::SetToken(napi_env env, napi_callback_info info)
{
    size_t argc = 2;
    size_t charLen = 0;
    int len = 1024;
    napi_value args[2] = {nullptr};
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    void *data;
    size_t dataLen;
    size_t byteOffset;
    napi_typedarray_type dataType;
    napi_value inputBuffer;
    napi_get_typedarray_info(env, args[0], &dataType, &dataLen, &data, &inputBuffer, &byteOffset);
    uint8_t *dataTmp = (uint8_t *)(data);
    if (dataTmp == nullptr || dataType != napi_uint8_array) {
        LOGI("SetToken dataTmp null");
        return nullptr;
    }
    // 获取当前类唯一标识classId
    char classId[1024] = {0};
    napi_get_value_string_utf8(env, args[1], classId, len, &charLen);
    std::string classIdStr = classId;
    Client *client = getClient(classIdStr);
    if (!client) {
        return nullptr;
    }
    SetNativeToken(client, dataTmp, dataLen);
    napi_value result = nullptr;
    napi_get_undefined(env, &result);
    return result;
}

napi_value Client::SetBlockMode(napi_env env, napi_callback_info info)
{
    size_t argc = 2;
    size_t charLen = 0;
    int len = 1024;
    int blockMode = COAP_BLOCK_USE_LIBCOAP;
    napi_value args[2] = {nullptr};
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    napi_get_value_int32(env, args[0], &blockMode);
    // 获取当前唯一标识classId
    char classId[1024] = {0};
    napi_get_value_string_utf8(env, args[1], classId, len, &charLen);
    std::string classIdStr = classId;
    Client *client = getClient(classIdStr);
    if (!client) {
        return nullptr;
    }
    LOGI("SetBlockMode set mode %d", blockMode);
    client -> blockMode = blockMode;
    napi_value result = nullptr;
    napi_get_undefined(env, &result);
    return result;
}

napi_value Client::SetSessionMtu(napi_env env, napi_callback_info info)
{
    size_t argc = 2;
    size_t charLen = 0;
    int len = 1024;
    int SessionMtu = COAP_DEFAULT_MTU;
    napi_value args[2] = {nullptr};
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    napi_get_value_int32(env, args[0], &SessionMtu);
    // 获取当前唯一标识classId
    char classId[1024] = {0};
    napi_get_value_string_utf8(env, args[1], classId, len, &charLen);
    std::string classIdStr = classId;
    Client *client = getClient(classIdStr);
    if (!client) {
        return nullptr;
    }
    LOGI("SetSessionMtu set SessionMtu %d", SessionMtu);
    client -> SessionMtu_ = SessionMtu;
    napi_value result = nullptr;
    napi_get_undefined(env, &result);
    return result;
}

napi_value Client::SetSessionRecvBufferSize(napi_env env, napi_callback_info info)
{
    size_t argc = 2;
    size_t charLen = 0;
    int len = 1024;
    int SessionRecvBufferSize = COAP_RXBUFFER_SIZE;
    napi_value args[2] = {nullptr};
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    napi_get_value_int32(env, args[0], &SessionRecvBufferSize);
    // 获取当前唯一标识classId
    char classId[1024] = {0};
    napi_get_value_string_utf8(env, args[1], classId, len, &charLen);
    std::string classIdStr = classId;
    Client *client = getClient(classIdStr);
    if (!client) {
        return nullptr;
    }
    LOGI("SetSessionRecvBufferSize set SessionRecvBufferSize %d", SessionRecvBufferSize);
    client -> SessionRecvBufferSize_ = SessionRecvBufferSize;
    napi_value result = nullptr;
    napi_get_undefined(env, &result);
    return result;
}

napi_value Client::SetPayload(napi_env env, napi_callback_info info)
{
    char content[2048] = {0};
    size_t argc = 2;
    size_t charLen = 0;
    int len = 1024;
    napi_value args[2] = {nullptr};
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    napi_get_value_string_utf8(env, args[0], content, len, &charLen);
    LOGI("payload:%s", content);
    // 获取当前类唯一标识classId
    char classId[1024] = {0};
    napi_get_value_string_utf8(env, args[1], classId, len, &charLen);
    std::string classIdStr = classId;
    Client *client = getClient(classIdStr);
    if (!client) {
        return nullptr;
    }
    SetInput(static_cast<char *>(content), &client->payload);
    napi_value result = nullptr;
    napi_get_undefined(env, &result);
    return result;
}

static int CreateTsFunction(napi_env env, Client *client, napi_value param)
{
    LOGI("Request needCallBack");
    client->needCallBack = true;
    napi_valuetype valueType = napi_undefined;
    napi_typeof(env, param, &valueType);
    if (valueType == napi_function) {
        napi_value name;
        napi_create_string_utf8(env, "message", NAPI_AUTO_LENGTH, &name);
        if (napi_create_threadsafe_function(env, param, nullptr, name, 0, 1, nullptr, thread_finalize_cb,
                                            nullptr, threadsafe_function_call_js, &client->_callbackFunc) != napi_ok) {
                LOGE("napi_create_threadsafe_function failed!");
                return 1;
        }
    }
    return 0;
}

napi_value Client::Request(napi_env env, napi_callback_info info)
{
    size_t argc = MAX_PARAM_NUM;
    napi_value args[MAX_PARAM_NUM] = {nullptr};
    int positionTwo = 2;
    int positionThree = 3;
    int positionFour = 4;
    int positionFive = 5;
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    size_t charLen = 0;
    int len = 1024;
    int coaptype = COAP_MESSAGE_CON;
    int coapMethod = COAP_REQUEST_GET;
    // 获取当前类唯一标识classId
    char classId[1024] = {0};
    napi_get_value_string_utf8(env, args[0], classId, len, &charLen);
    std::string classIdStr = classId;
    Client *client = getClient(classIdStr);
    if (!client) {
        return nullptr;
    }
    napi_get_value_string_utf8(env, args[1], client->serverUri, len, &charLen);
    napi_get_value_int32(env, args[positionTwo], &coapMethod);
    client->method = coapMethod;
    napi_get_value_int32(env, args[positionThree], &coaptype);
    client->msgType = coaptype;
    if (argc == positionFive) {
        CreateTsFunction(env, client, args[positionFour]);
    }
    napi_deferred deferred;
    napi_value promise;
    napi_create_promise(env, &deferred, &promise);
    AsyncCallbackInfo *asyncCallbackInfo = new AsyncCallbackInfo {
        .env = env,
        .asyncWork = nullptr,
        .deferred = deferred,
        .client = client
    };
    napi_value resourceName;
    napi_create_string_latin1(env, "CoapRequest", NAPI_AUTO_LENGTH, &resourceName);
    uint32_t count;
    napi_reference_ref(env, asyncCallbackInfo->client->jsRef, &count);
    napi_create_async_work(
        env, nullptr, resourceName,
        [](napi_env env, void *data) {
            AsyncCallbackInfo *asyncCallbackInfo = (AsyncCallbackInfo *)data;
            RequestNative(asyncCallbackInfo->client);
        },
        [](napi_env env, napi_status status, void *data) { DealCallBack(env, (AsyncCallbackInfo *)data); },
        (void *)asyncCallbackInfo, &asyncCallbackInfo->asyncWork);
    napi_queue_async_work(env, asyncCallbackInfo->asyncWork);
    return promise;
}

/** Returns true iff @p number denotes an option number larger than 255. */
static bool is_long_option(coap_option_num_t number) { return number > 255; }

napi_value Client::RegisterOption(napi_env env, napi_callback_info info)
{
    size_t argc = 2;
    size_t charLen = 0;
    int len = 1024;
    napi_value args[2] = {nullptr};
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    int opt = COAP_OPTION_IF_MATCH;
    napi_get_value_int32(env, args[0], &opt);
    // 获取当前类唯一标识classId
    char classId[1024] = {0};
    napi_get_value_string_utf8(env, args[1], classId, len, &charLen);
    std::string classIdStr = classId;
    Client *client = getClient(classIdStr);
    if (!client) {
            return nullptr;
    }
    int shortCount = 0;
    int longCount = 0;
    for (auto iter = client->options.begin(); iter != client->options.end(); iter++) {
            uint16_t opt = *iter;
            if (is_long_option(opt)) {
            ++longCount;
            } else {
            ++shortCount;
            }
    }
    if (longCount >= COAP_OPT_FILTER_LONG || shortCount >= COAP_OPT_FILTER_SHORT) {
            napi_value result_error = nullptr;
            napi_get_undefined(env, &result_error);
            return result_error;
    }
    client->options.push_back(opt);
    LOGI("register coap known option: %d", opt);
    napi_value result = nullptr;
    napi_get_undefined(env, &result);
    return result;
}
